package service

import (
	"context"
	"database/sql"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"strconv"

	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/os/gtime"
)

type MyDriver struct {
	*gdb.DriverMysql
}

var (
	customDriveName = "MyDriver"
)

func init() {
	if err := gdb.Register(customDriveName, &MyDriver{}); err != nil {
		panic(err)
	}
}

func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
	return &MyDriver{
		&gdb.DriverMysql{
			Core: core,
		},
	}, nil
}

func (d *MyDriver) DoQuery(ctx context.Context, link gdb.Link, sql string, args ...interface{}) (result gdb.Result, err error) {
	tsMilli := gtime.TimestampMilli()
	rows, err := d.DriverMysql.DoQuery(ctx, link, sql, args...)

	//Do it what you want
	link.Exec(
		"INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)",
		gdb.FormatSqlWithArgs(sql, args),
		gtime.TimestampMilli()-tsMilli,
		gtime.Now(),
		err,
	)
	return rows, err
}
func (d *MyDriver) DoExec(ctx context.Context, link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) {
	tsMilli := gtime.TimestampMilli()
	result, err = d.DriverMysql.DoExec(ctx, link, sql, args...)

	//Do it what you want
	link.Exec(
		"INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)",
		gdb.FormatSqlWithArgs(sql, args),
		gtime.TimestampMilli()-tsMilli,
		gtime.Now(),
		err,
	)
	return
}

func (d *MyDriver) DoFilter(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
	//Filter sql Step1
	sql_new := gstr.Replace(sql, "\n", "")

	//Filter sql Step2
	sql_new = gstr.Replace(sql_new, "\t", "")
	//... Filter what you want ...

	//Filter args Step1
	for _, v := range args {
		switch v.(type) {
		case gdb.Map:
			//Do it what you wan
		case string:
			//Do it what you wan
		}
	}
	return sql_new, args, nil
}

func (d *MyDriver) ConvertDataForRecord(ctx context.Context, data interface{}) map[string]interface{} {
	//this hook is convert data to map[string]interface{}
	result := make(map[string]interface{}, 0)

	//like this
	switch data.(type) {
	case gdb.Map:
		result = gconv.Map(data)
	case gdb.List:
		for k, v := range data.(gdb.List) {
			result[strconv.Itoa(k)] = gconv.Map(v)
		}
		//case other type,do it what you want
	}
	return result
}
